package com.huisenky.smallidea.convert.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.huisenky.smallidea.common.util.BeeUtils;
import com.huisenky.smallidea.convert.annotation.SmallResEnum;
import com.huisenky.smallidea.convert.annotation.SmallResField;
import com.huisenky.smallidea.convert.annotation.SmallResInject;
import com.huisenky.smallidea.convert.enums.InjectType;
import com.huisenky.smallidea.convert.model.InjectCacheKey;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class ConvertUtil {

    private ConvertUtil() {
    }

    /*
    注解写在 source上
     */
    public static <T, E> E convert(T source, Class<E> clazz) {
        Map<InjectCacheKey, Object> injectCache = new HashMap<>();
        E target = BeeUtils.copyProperties(source, clazz);
        Field[] fields = ReflectUtil.getFields(source.getClass());
        for (Field field : fields) {
            // 常规转换 转date 没有注解也会转 注解只是决定来 format格式
            ConvertUtil.normalConvert(source, target, field);
            // 被@SmallResEnum标注的转换
            convertIfEnum(source, target, field);
            // 被@SmallResInject标注的转换
            convertIfInject(source, target, field, injectCache);
        }
        return target;
    }

    private static void normalConvert(Object source, Object target, Field field) {
        if (Date.class.equals(field.getType())) {
            Date date = (Date) ReflectUtil.getFieldValue(source, field);
            SmallResField smallResField = field.getAnnotation(SmallResField.class);
            if (smallResField != null && StrUtil.isNotBlank(smallResField.format())) {
                try {
                    setFieldValue(target, field.getName(), DateUtil.format(date, smallResField.format()));
                } catch (Exception e) {
                    setFieldValue(target, field.getName(), "");
                }
            } else {
                setFieldValue(target, field.getName(), DateUtil.format(date, DatePattern.NORM_DATETIME_FORMAT));
            }
        }
    }

    private static void convertIfEnum(Object source, Object target, Field field) {
        SmallResEnum smallResEnum = field.getAnnotation(SmallResEnum.class);
        if (smallResEnum != null) {
            Object fieldValue = ReflectUtil.getFieldValue(source, field);
            if (fieldValue != null) {
                Class<?> enumClassName = smallResEnum.value();
                String methodName = smallResEnum.method();
                Method enumMethod = ReflectUtil.getMethodByName(enumClassName, methodName);
                if (enumMethod != null && isPublicStatic(enumMethod)) {
                    Object targetFieldValue = invokeStatic(enumMethod, fieldValue);
                    if (targetFieldValue != null) {
                        setTargetValue(target, smallResEnum.suffix(), field.getName(), targetFieldValue);
                    }
                }
            }
        }
    }

    private static void convertIfInject(Object source, Object target, Field field, Map<InjectCacheKey, Object> injectCache) {
        SmallResInject smallResInject = field.getAnnotation(SmallResInject.class);
        if (smallResInject != null) {
            Object fieldValue = ReflectUtil.getFieldValue(source, field);
            if (fieldValue != null) {
                Class<?> injectorClass = smallResInject.value();
                Object injector = ReflectUtil.newInstance(injectorClass);
                Object res = null;
                if (injectCache.get(new InjectCacheKey(injectorClass, fieldValue)) == null) {
                    // injector.inject(fieldValue)：上游字段值为形参，调用injector的inject方法
                    res = ReflectUtil.invoke(injector, "inject", fieldValue,field.getName());
                    // injector + 字段值为key，注入值为value
                    injectCache.put(new InjectCacheKey(injectorClass, fieldValue), res);
                } else {
                    res = injectCache.get(new InjectCacheKey(injectorClass, fieldValue));
                }
                //newValue 优先级最大
                if (StrUtil.isBlank(smallResInject.newValue())){
                    InjectType injectType = smallResInject.injectType();
                    String injectFieldName = ReflectUtil.invoke(ReflectUtil.newInstance(injectType.getInjectSolution()), "injectField", field.getName());
                    setFieldValue(target, injectFieldName, res);
                }else {
                    setFieldValue(target, smallResInject.newValue(), res);
                }
            }
        }
    }

    private static void setTargetValue(Object target, String[] suffixes, String fieldName, Object value) {
        for (String targetSuffix : suffixes) {
            setFieldValue(target, fieldName + targetSuffix, value);
        }
    }

    private static boolean isPublicStatic(Method method) {
        return method.getModifiers() == Modifier.PUBLIC + Modifier.STATIC;
    }

    private static Object invokeStatic(Method method, Object arg) {
        try {
            return ReflectUtil.invokeStatic(method, arg);
        } catch (Exception e) {
            return null;
        }
    }

    private static void setFieldValue(Object target, String fieldName, Object targetFieldValue) {
        try {
            ReflectUtil.setFieldValue(target, fieldName, targetFieldValue);
        } catch (Exception e) {
            // 忽略
        }
    }
}
